#pragma once
#include <IShader.h>

#include "Bone.h"
#include "Mesh.h"
#include "ModelAnimation.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/euler_angles.hpp>
// include fdualquat
#include <glm/gtx/dual_quaternion.hpp>
#include <assimp/scene.h>

struct Bone;
struct ArrayBone;
class ModelAsset;
class SkeletonAsset;
class SkeletonManager;
class MeshInstance;

struct OrderedAnimation;
class OrderedMesh;

struct BoneAsset
{
    std::string name;
    int animId = -1;
    int skinId;
    int id;
    std::string parentName;
    std::list<std::string> childrenNames;
    glm::mat4 restPose;
    glm::mat4 bindPose;
    glm::mat4 restPoseInv;
    bool animated;
    bool skinned;
    BoneAsset(std::string name, int id, std::string parentName, glm::mat4 restPose, glm::mat4 bindPose, glm::mat4 restPoseInv)
        : name(name), id(id), parentName(parentName), restPose(restPose), bindPose(bindPose), restPoseInv(restPoseInv)
    {
        skinned = false;
        animId = -1;
        animated = false;
        skinId = -1;
        if (name.find("s-") == 0)
            skinned = true;

        if (name.find("a-") == 0)
            animated = true;

        if (name.find("a-s-") == 0 || name.find("s-a-") == 0) {
            skinned = true;
            animated = true;
        }
    }
};

enum MeshInstanceType
{
    Head = 1,
    Torso = 2,
    Hip = 4,
    Thigh = 8,
    Leg = 16,
    Foot = 32,
    UpperArm = 64,
    LowerArm = 128,
    Hand = 256,
};

class Skeleton
{
public:
    std::string name;
    // 我们需要知道自己是以那种SkeletonAsset为模板构造的
    std::shared_ptr<SkeletonAsset> skeletonAsset;
    SkeletonManager *skeletonManager;

    uint32_t BodyMask;
    void DrawBodyMask(const MeshInstanceType &type)
    {
        // BodyMask 于 type 为一的位置为1, 其他保留
        BodyMask = BodyMask | type;
    }
    void HideBodyMask(const MeshInstanceType &type)
    {
        // BodyMask 于 type 为一的位置为0, 其他保留
        BodyMask = BodyMask & (~type);
    }

    // std::shared_ptr<Bone> rootBone;
    // std::vector<std::shared_ptr<Bone>> allBones;

    std::vector<ArrayBone> arrayBones;
    std::vector<int> animBonesIndices;
    std::vector<int> skinBonesIndices;

    std::vector<bool> arrayBonesFlag;

    glm::mat4 offset = glm::scale(glm::mat4(1.0f), glm::vec3(5.0f));
    std::vector<glm::mat4> CurrentPose;

    bool toDraw = true;
    // 骨架可以借此得知自己的骨骼状态储存在贴图的哪一行
    // 如果是-1，则代表它没准备好渲染
    int drawId = -1;
    std::vector<glm::mat4> CurrentBindPose;

    Skeleton(std::string name, std::shared_ptr<SkeletonAsset> asset, SkeletonManager *manager);

    ~Skeleton()
    {
        // allBones.clear();
    }
    void UpdateInner(int id, std::shared_ptr<std::vector<glm::mat4>> anim);

    void UpdateOffset(std::shared_ptr<std::vector<glm::mat4>> anim);

    void BindMesh(std::shared_ptr<MeshInstance> mesh)
    {
        bindMeshes.push_back(mesh);
    }

    void PlayAnimation(const std::string &animName, uint32_t tick);
    void Play2BlendAnimation(const std::string &animName, const std::string &animName2, uint32_t tick, float blend);

    // 单线程准备
    void PrepareToDraw();

    // 多线程调用
    void ProcessManagerData();

    // 单线程处理结果
    void ProcessAllMeshInstance();

private:
    std::list<std::shared_ptr<MeshInstance>> bindMeshes;
};

class SkeletonAsset
{
public:
    std::string SkeletonType = "UnNamed";
    std::string rootBoneName;
    std::unordered_map<std::string, std::shared_ptr<BoneAsset>> bones;

    // 全部的骨骼名对应id
    std::unordered_map<std::string, int> boneNameIndex;

    // 需要播放动画的骨骼的id应该从0开始连续排列，不应该有非动画骨骼混在其中。
    // 这样是为了方便生成动画frame
    // 在有动画的骨骼排列完毕后再排列没有动画的骨骼id
    std::unordered_map<std::string, int> animBoneNameIndex;
    std::unordered_map<std::string, int> skinBoneNameIndex;

    SkeletonAsset(std::string name, const aiNode *rootBone, ModelAsset *model);

    std::unordered_map<std::string, std::shared_ptr<OrderedAnimation>> orderedAnimations;
    std::unordered_map<std::string, std::shared_ptr<OrderedMesh>> orderedMeshes;

    // 创建一个骨架实例
    // std::shared_ptr<Skeleton> CreateSkeleton();

    void Export(std::string filename);

private:
    void CollectBone(const aiNode *node, const aiNode *parent, ModelAsset *model);
};

class SkeletonManager
{
public:
    bool useDQBSkinning = true;

    friend class Skeleton;

    std::list<std::shared_ptr<Skeleton>> skeletons;

    SkeletonManager(std::shared_ptr<SkeletonAsset> asset);

    std::shared_ptr<Skeleton> CreateSkeleton();

    // 在绘制之前，我们询问每个骨架实例是否需要渲染，
    // 如果需要渲染，则取它的骨架变换更新动画贴图，
    // 并告诉它，自己的transform在第几行（drawid）
    void UpdateAnimTexture();

    void PrepareInstanceDraw(const gl::IShader &shader) const;

private:
    uint32_t width = 1024, height = 1024;
    std::shared_ptr<SkeletonAsset> asset;
    uint32_t AnimTransformTexture;
    std::vector<float> AnimTransformData;
    uint32_t instanceDrawCount = 0;
};
